#!/bin/bash 
#
# https://github.com/coderofsalvation/testosteron
#
# Edited by YihaoPeng <yihao.peng@bitmain.com>
#
# Copyright 2014 Coder of Salvation. All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
# 
#    1. Redistributions of source code must retain the above copyright notice, this list of
#       conditions and the following disclaimer.
# 
#    2. Redistributions in binary form must reproduce the above copyright notice, this list
#       of conditions and the following disclaimer in the documentation and/or other materials
#       provided with the distribution.
# 
# THIS SOFTWARE IS PROVIDED BY Coder of Salvation AS IS'' AND ANY EXPRESS OR IMPLIED
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Coder of Salvation OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# 
# The views and conclusions contained in the software and documentation are those of the
# authors and should not be interpreted as representing official policies, either expressed
# or implied, of Coder of Salvation 
# 
SELF_PID=$$
SELF_PATH="$(dirname "$(readlink -f "$0")" )"; cd "$SELF_PATH"
if [ "x$LOGFILE" = "x" ]; then
  LOGFILE="/tmp/testosteron.$SELF_PID.log"
fi
TMPFILE="/tmp/testosteron.$SELF_PID"
export LC_ALL=C # needed so unix 'sort' works properly with '-' and numbers
# these vars keep track of most important things
FAIL=0; PASS=0; FILES=()

# colors 
C_NORMAL="\\033[0;0m"; C_BOLD="\E[1;37;40m"; C_BRIGHT="\E[1;33;40m"
C_GREEN="\\033[1;32m"; C_RED="\\033[0;31m";  C_GREY="\\033[1;30m"
C_RESET="\\033[0m"

##########################################################
# Utility functions

_tprintf(){
  LABEL="testosteron> " _printf "$1"
}

_printf(){
  [[ -n $LABEL ]] && label="$LABEL" || label="$(cat /proc/loadavg | awk '{ print $1" "$2; }') $(free -t | grep Total | awk '{ printf $3}') $(date +%T.%03N)"
  printf "$C_GREY""%s""$C_NORMAL %s\n" "$label" "$1"
}

_printcolumn(){
  printf "$C_GREY""%-4s %-4s %-8s %s\n" "cpu"  "i/o" "memory" "time"
  printf "$C_GREY""____ ____ ________ ____________\n"
}

_cleanup(){
  [[ -f $TMPFILE.fail ]] && exitcode=1 || exitcode=0
  rm -f $TMPFILE.* &>/dev/null
  exit $exitcode
}

_init(){
  :>$LOGFILE 
}

_throw_error(){
  ((FAIL=FAIL+1)); ERRORFILES+=("$1")
  printf "$C_RED""ERROR: $C_BOLD :(\n"
  # whatever happens, testosteron did not pass all tests if this file exists
  touch $TMPFILE.fail
}

_throw_error_background(){
  printf "$C_RED""ERROR: $C_BOLD :(\n"
  # whatever happens, testosteron did not pass all tests if this file exists
  touch $TMPFILE.fail
  # log error files
  echo "$1" >> $TMPFILE.errorfiles
}

_load_background_error(){
  if [ -f $TMPFILE.errorfiles ]; then
    ((FAIL=FAIL+1))
    ERRORFILES+=(`cat $TMPFILE.errorfiles`)
    rm $TMPFILE.errorfiles
  fi
}

_readfiles(){
  find -L "$1" -type f | sort -n  > $TMPFILE.files 
  while read file; do [[ -x "$file" ]] && FILES+=("$(readlink -f "$file")"); done < $TMPFILE.files
}

_overview(){
  total=${#FILES[@]}
  succes=$(( total - FAIL ));
  _tprintf "tests executed : $total";
  _tprintf "tests fail     : $FAIL";
  for file in "${ERRORFILES[@]}"; do 
    _tprintf "  -> $file";
  done
}

_usage(){
  echo "Usage: "
  grep "^[^_].\+(){$" $0 | while read line; do
    local cmd=$(echo "$line" | sed "s/(){//g")
    local info=$(grep -C0 -A0 -B1 "$cmd(){" $0 | sed "N;s/\n.*//g" )
    printf "    $0 %-20s %-40s\n" "$cmd" "${info/\# /}" 
  done; echo "";
}

# filters out escape codes
_filterescape(){
  sed -i -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" "$1"
}

##########################################################
# Application functions

# <dir>        runs all tests found in dirs (recursively)
rundir(){
  dir="$1";
  [[ ! -d "$dir" ]] && { _tprintf "$dir: no such dir"; exit 1; }
  _readfiles "$dir"
  for file in "${FILES[@]}"; do run "$file"; done 
  _overview
  exit 0
}

# <file1> ...  runs one or more tests
runsome(){
  if [ "x$*" == "x" ]; then
    { _tprintf "$dir: no test cases specified"; exit 1; }
  fi
  FILES=("$@")
  for file in "${FILES[@]}"; do run "$file"; done 
  _overview
  exit 0
}

# <file>       runs one test 
run(){
  file="$(basename "$1")"; path="$(dirname "$1" )";
  printf "$C_BOLD""==> ""$C_GREEN"" $file $C_RESET\n\n"
  touch $TMPFILE.out

  cd "$path"

  # run in the background
  { ${PREFIX} ./$file || _throw_error_background "$file";} &>$TMPFILE.out &

  # wait for background job
  PID=$!
  _printcolumn;
  tail -f -n +1 --pid=$PID $TMPFILE.out | while read line; do _printf "$line"; done
  echo ""
  _load_background_error

  [[ -f "$file.out" ]] && { 
    if ! diff "$file.out" "$TMPFILE.out"; then _throw_error "$file (output does not match output $file.out)";
    else echo "OK: output matches $file.out" >> $TMPFILE.out; fi
  }
  cd "$SELF_PATH" &>/dev/null
}

##########################################################
# Main entry 

trap _cleanup SIGTERM 0 1 2 3 15
{
  _init && [[ -n "$1" ]] && "$@" || _usage
} | tee -a $LOGFILE
_filterescape "$LOGFILE"
  
